/* 
 * File:   CollisionHandler.h
 * Author: RedEyedKiller
 *
 * Created on 21 Απρίλιος 2011, 2:21 μμ
 */

#ifndef COLLISIONHANDLER_H
#define	COLLISIONHANDLER_H

#include "../PhysicsLogic.h"
#include "CollisionEvents.h"
#include "Contact.h"
#include <queue>

//predefinitions
class LevelMap;
class TileProperties;

using EntitySystem::PhysicsLogic;

namespace physicsSystem
{
namespace PhysicsInternals
{

/**
 * A class used by physics manager to handle all collision logic.
 */
class CollisionHandler
{
public:
    typedef std::vector<EntitySystem::PhysicsLogic*> PhysicsPool;
    typedef std::vector<RDCBoundaryEntry> BoundaryList;

    CollisionHandler(int entityollisionUpdateTime);
    ~CollisionHandler();

    /**
     * All collision checks are performed in this method.
     */
    void CollisionCheck(PhysicsPool& pool, LevelMap* currentMap, TileProperties* properties, float duration);

protected:
    /**
     * Resolves all entity per entity collsion events.
     */
    void ColisionResolution(float duration);

    /**
     * Called by TileCollisionResolution for each event.
     * @param eventIt - The event to handle.
     * @param pObject - The oBject produced the event.
     * @param properties - The tile properties.
     */
    void TileCollision(const TileColEvent& eventIt, EntitySystem::PhysicsLogic* pObject, TileProperties* properties);

    /**
     * Called for each entity from within TileCollisionCheck and iterates through out all 
     * TileColEvents in the buffer. If empty collision NONE is invoked.
     * @param pObject - The object that produced the events in the buffer. (if any)
     * @param eventBuffer - The collection with all the collisions pObject produced.
     * @param properties
     */
    void TilesCollisionResolution(EntitySystem::PhysicsLogic* pObject,
                                  const std::vector<TileColEvent>& eventBuffer, TileProperties* properties);

    /**
     * Check which pairs of entities collide and fills up the entityCollisionBuffer.
     * This is the brute force step of RDC. Instead of using a pure brute force algorithm
     * this method takes andvantage of the (required by the RDC) sorted list.
     * @param group
     * @param sortAxis - the axis the last sort happened.
     */
    void BruteForceEntityCheck(PhysicsPool& group, Axis sortAxis, int groupIndex);

    /**
     * The core of the RDC  ( Recursive Dimensional Clustering (wow!) ) algorithm.
     * It analyzes the current group on the given axis.
     * If the group is canot divede by the given axis proceeds to the next one.
     * If the group can be divided breaks it into two groups and for each group 
     * restarts the algorithm.
     * If the given currentAxis is Invalid or the group is to small BruteForce is called 
     * to finish up the work.
     * 
     * @param group
     * @param lastAxis - Used to determine sortAxis for BruteForceEntityCheck. It the last currentAxis.
     * @param currentAxis - The axis for the current step
     * @param futureAxis - The axis fot the next step.
     */
    int EntityPerEntityRDC(PhysicsPool& group, int groupIndex, Axis lastAxis, Axis currentAxis, Axis futureAxis);

    /**
     * Sorts the given boundary list.
     * @param list
     */
    void SortBoundaryList(BoundaryList& list);

    /**
     * Creates the boundarylist of the given group on the given axis.
     * 
     * A boundary list for each object in the group creates two entires:
     * One OPEN with the the minimum point of the object on the given axis.
     * One CLOSE with the maximum point of the object on the given axis.
     * 
     * @param group
     * @param list
     * @param axis - The axis on which the list will be made.
     */
    void InitializeBoundaryList(PhysicsPool& group, BoundaryList& list, Axis axis);

    /**
     * Checks for each entity if it collides with a tile. Afterwrds TileCollisionResolution is called
     * to handle the collision events if any.
     * @param currentMap - The map against which the handler will check.
     * @param pool - The container with all entities. (sorting is not required)
     * @param properties - The object that describes the properties of tiles.
     */
    void EntityPerTileCheck(LevelMap* currentMap, PhysicsPool& pool, TileProperties* properties);

    /**
     * Finalizes the EntityPerTileCheck by pushing the remaining events.
     */
    void FinalizeTileCollisionDetection(std::vector<TileColEvent>& eventBuffer,
                                        const TileColEvent& horizontialRegion,
                                        TileColEvent * const verticalRegion, int colms, int tileW);

    /**
     * Performs all required operations in order to begin entity per entity collision checks.
     * --Sorts the physics pool so that objects with smaller x position will be on the top
     * of the vector.
     */
    void PreaparePool(PhysicsPool& pool);

private:

    /**
     * The minimum size of a group in the RDC algorithm where the BruteForces handles the rest.
     * (if the size exceeds this number RDC will call BruteForce ven if the group is 
     * not enitrely an undividable one)
     */
    const unsigned int RDC_SIZE_LIMIT;

    /**
     * maximum number of iterations of contract resolving algorithm 
     * is this const * number of contacts.
     */
    const int ITERAT_MAX_MOD;

    /**
     * The max seperation velocity the contact resolving algorithm will try to resolve
     */
    const float SEP_VEL_TOL;

    /**
     * The minimum penetration the contact resolving algorithm will try to resolve.
     */
    const float PENET_TOL;
    
    /**
     * A buffer to store entity per entity collision events.
     */
    std::vector<Contact> entityCollisionBuffer;
    /**
     * Minimum miliseconds between two sequential call of entity per entity collision
     */
    long entityollisionUpdateTime;

    /**
     * The global duaration between two frames. 
     * Reseted at each CollisionCheck call.
     */
    float duration;

};

struct Reseter : public std::unary_function<PhysicsLogic*, void>
{

    void operator() (PhysicsLogic * object)
    {
        object->GetRect()->SetOrigin(object->GetPosition().ConvertTo<int>());
        object->SetGroupIndex(-1);
        object->ResetContactList();
    }
};

};
};
#endif	/* COLLISIONHANDLER_H */

